package cn.jingyuan.owl.utils.spring.web;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

// fixme 重新梳理并提供方法 参考 javax.servlet.http.HttpServletRequest javax.servlet.ServletRequest
public class SpringWebUtils {
    
    private SpringWebUtils() {
        throw new IllegalStateException("Utility class");
    }
    
    public static HttpServletRequest getHttpServletRequest() {
        HttpServletRequest request = null;
        try {
            request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        } catch (Exception e) {
            // nothing to do
        }
        return request;
    }
    
    public static String getContextPath() {
        return getContextPath(getHttpServletRequest());
    }
    
    public static String getContextPath(HttpServletRequest request) {
        String contextPath = null;
        if (null != request) {
            contextPath = request.getContextPath();
        }
        return contextPath;
    }
    
    public static String getServerUrl() {
        return getServerUrl(getHttpServletRequest());
    }
    
    public static String getServerUrl(HttpServletRequest request) {
        String serverUrl = null;
        if (null != request) {
            serverUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
        }
        return serverUrl;
    }
    
    public static Map<String, String> getHeaders() {
        return getHeaders(getHttpServletRequest());
    }
    
    public static Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> headers = Collections.emptyMap();
        if (null != request) {
            headers = new LinkedHashMap<>();
            
            Enumeration<String> enumeration = request.getHeaderNames();
            if (enumeration != null) {
                while (enumeration.hasMoreElements()) {
                    String name = enumeration.nextElement();
                    String value = request.getHeader(name);
                    headers.put(name, value);
                }
            }
        }
        return headers;
    }
    
    public static String getHeader(String name) {
        return getHeader(name, getHttpServletRequest());
    }
    
    public static String getHeader(String name, HttpServletRequest request) {
        Map<String, String> headers = getHeaders(request);
        return headers.get(name);
    }
    
    /**
     * 是否是 Ajax 异步请求
     *
     * @param request request
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        return
            (request.getHeader("X-Requested-With") != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With")))
                ||
            (request.getHeader("Content-Type") != null && request.getHeader("Content-Type").startsWith("application/json"));
    }
    
    /**
     * 获取 ip 地址
     */
    public static String getRemoteAddress() {
        return getRemoteAddress(getHttpServletRequest());
    }
    
    /**
     * 获取 ip 地址
     */
    public static String getRemoteAddress(HttpServletRequest request) {
        String unknown = "unknown";
        String ip = request.getHeader("X-Forwarded-For");
        
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        
        // 对于通过多个代理的情况，第一个IP为客户端真实IP，多个 IP 按照  ','  分割
        if (ip != null && ip.length() > 0) {
            String[] ips = ip.split(",");
            if (ips.length > 0) {
                ip = ips[0];
            }
        }
        
        return ip;
    }
    
    /**
     * 获取请求体
     */
    public static String getBody(final ServletRequest request) {
        StringBuilder builder = new StringBuilder();
        
        try (
            InputStream inputStream = cloneInputStream(request.getInputStream());
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
        ) {
            String line = "";
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        return builder.toString();
    }
    
    /**
     * 复制输入流
     *
     * @param inputStream 流
     */
    public static InputStream cloneInputStream(ServletInputStream inputStream) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        
        try {
            while ((length = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, length);
            }
            byteArrayOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
    }
    
}
